package com.qf.websocket;

import com.qf.events.CommandEvent;
import com.qf.events.DeviceOnOffLineEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * @Author ztf
 * @Date 21:14 2022/1/6
 **/
public class MyBaseCommandHandler extends TextWebSocketHandler {
    private ApplicationContext context;

    @Autowired
    public void setContext(ApplicationContext context) {
        this.context = context;
    }

    /**
     * 保存有所有客户端的map对象
     */
    private static Map<String, WebSocketSession> allClients = new HashMap<>();

    private static Map<String, Long> allClientLastHeartbeat = new HashMap<>();

    public static Map<String, WebSocketSession> getAllClients() {
        return allClients;
    }

    public static Map<String, Long> getAllClientLastHeartbeat() {
        return allClientLastHeartbeat;
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //保存连接
        String uri = session.getUri().toString();
        //地址最后一位为设备标识
        String deviceId = uri.substring(uri.lastIndexOf("/") + 1);
        WebSocketSession webSocketSession = allClients.get(deviceId);
        if (webSocketSession != null) {
            //设备多次连接的情况下，先关闭原先的连接，减少资源占用
            webSocketSession.close();
        }
        //保存到会话中
        session.getAttributes().put("id", deviceId);
        //将设备的链接保存起来
        allClients.put(deviceId, session);
        System.err.println("设备" + deviceId + "连接成功");
        //保存设备最后一次心跳
        allClientLastHeartbeat.put(deviceId, System.currentTimeMillis());
        context.publishEvent(new DeviceOnOffLineEvent(deviceId, new Date(), session.getRemoteAddress().getHostString(), (short) 1));
        super.afterConnectionEstablished(session);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        super.handleTextMessage(session, message);
        //设备id
        Object id = session.getAttributes().get("id");
        //获取设备发送的内容
        String payload = message.getPayload();
        System.err.println("收到设备：" + id + "发送的数据:" + payload);
        //设备无论发送什么都属于心跳
        allClientLastHeartbeat.put((String) id, System.currentTimeMillis());
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        context.publishEvent(new DeviceOnOffLineEvent((String) session.getAttributes().get("id")
                , new Date()
                , null
                , (short) 2));
    }

    public static void sendControl(CommandEvent event){
        Object command = event.getCommand();
        String uid = event.getUid();
        WebSocketSession webSocketSession = allClients.get(uid);
        if (webSocketSession!=null&&webSocketSession.isOpen()){
            try {
                //发送命令
                webSocketSession.sendMessage(new TextMessage(command.toString()));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void checkStatus() {
        CompletableFuture.runAsync(() -> {
            HashMap<String, Long> hashMap = new HashMap<>();
            hashMap.putAll(allClientLastHeartbeat);//为了能够在里面一边遍历一边删除,我们使用了一个临时的map保存了所有的数据
            hashMap.forEach((id, time) -> {
                long currentTimeMillis = System.currentTimeMillis();
                if (currentTimeMillis - time >= 60000) {
                    //关闭会话
                    System.err.println("设备:" + id + "已经离线了");
                    WebSocketSession socketSession = allClients.get(id);//以设备的编号为key,获取到回话
                    if (socketSession != null && socketSession.isOpen()) {
                        try {
                            socketSession.close();//关闭会话,会触发上面的关闭方法,在内部会将设备变更为离线
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    allClients.remove(id);
                    allClientLastHeartbeat.remove(id);//会不会出现一边遍历一边删除的情况?
                }
            });
            hashMap.clear();
        });
    }
}
